Tutorial step 6: supporting user configuration |
The next logical step is allowing the user to choose between half and third color ramp reduction, and to enable or disable the 2x scaling. This requires two extra routines, configProc and stringProc.
![]() |
![]() Since Avisynth does not have a GUI, it cannot use these functions. Avisynth users will not be able to configure your filter at all if you do not provide a script interface! |
The configProc and stringProc functions |
configProc is simple: it brings up a dialog box. This requires a extra parameter to the usual template:
int configProc(FilterActivation *fa, const FilterFunctions *ff, HWND hwnd);
When called, configProc should open up a modal dialog box, usually with DialogBoxParam(). Return zero for OK, and non-zero for Cancel, but modify configuration parameters on OK only. VirtualDub doesn't care about the return parameter normally, except when a filter is first added to the chain; here VirtualDub calls configProc immediately, and if the user cancels the dialog box, the filter instance is destroyed.
Note: You'll probably need the HINSTANCE of your filter module to open the dialog box. Fortunately, VirtualDub provides this value for you as fa->filter->module->hInstModule, so you won't have to write a DLL entry routine to cache the instance pointer.
stringProc receives the address of a string buffer to fill:
void stringProc(const FilterActivation *fa, const FilterFunctions *ff, char *buf);
The purpose of stringProc is to give a quick overview of a filter instance's settings on the filter list. For instance:
320x240 640x480 resize (bilinear)
The buf parameter points right after the name, and the usual course of action is to supply the buffer to sprintf() or wsprintf(). By convention, filters should add the space and specify the parameters in parentheses.
Adding user configuration |
Create a resource file with a dialog IDD_FILTER_TUTORIAL. Leave the standard OK and Cancel boxes, create two checkboxes: IDC_EXPAND and IDC_THIRD. These constants should be defined in resource.h. If you are using Visual C++, this will be very easy to do.
Next, we'll need to update MyFilterData to hold the new fields.
typedef struct MyFilterData {
Pixel32 *grn_tab;
Pixel32 *blu_tab;
bool fExpand;
bool fThird;
} MyFilterData;
Now we need the configProc and stringProc functions. (You should know how to update the FilterDefinition by now.) If you're an MFC weenie, this is going to hurt. ^^ When creating more complex dialog boxes, you might find the need to store temporary items, such as GDI brush handles; the easiest way to do this is simply to stick them in your instance data structure.
BOOL CALLBACK tutorialConfigDlgProc(HWND hdlg, UINT msg, WPARAM wParam, LPARAM lParam) {
MyFilterData *mfd = (MyFilterData *)GetWindowLong(hdlg, DWL_USER);
switch(msg) {
case WM_INITDIALOG:
SetWindowLong(hdlg, DWL_USER, lParam);
mfd = (MyFilterData *)lParam;
CheckDlgButton(hdlg, IDC_EXPAND, mfd->fExpand?BST_CHECKED:BST_UNCHECKED);
CheckDlgButton(hdlg, IDC_THIRD, mfd->fThird?BST_CHECKED:BST_UNCHECKED);
return TRUE;
case WM_COMMAND:
switch(LOWORD(wParam)) {
case IDOK:
mfd->fExpand = !!IsDlgButtonChecked(hdlg, IDC_EXPAND);
mfd->fThird = !!IsDlgButtonChecked(hdlg, IDC_THIRD);
EndDialog(hdlg, 0);
return TRUE;
case IDCANCEL:
EndDialog(hdlg, 1);
return FALSE;
}
break;
}
return FALSE;
}
int tutorialConfigProc(FilterActivation *fa, const FilterFunctions *ff, HWND hwnd) {
return DialogBoxParam(fa->filter->module->hInstModule,
MAKEINTRESOURCE(IDD_FILTER_TUTORIAL), hwnd,
tutorialConfigDlgProc, (LPARAM)fa->filter_data);
}
void tutorialStringProc(const FilterActivation *fa, const FilterFunctions *ff, char *str) {
const char *modes[2][2]={
" (normal)",
" (expand)",
" (third)",
" (expand+third)",
};
MyFilterData *mfd = (MyFilterData *)fa->filter_data;
strcpy(str, modes[mfd->fThird][mfd->fExpand]);
}
Add the new functions to the FilterDefinition structure:
tutorialConfigProc, // configProc
tutorialStringProc, // stringProc
We need to rewrite paramProc as well, to switch between 1x and 2x modes.
long tutorialParamProc(FilterActivation *fa, const FilterFunctions *ff) {
MyFilterData *mfd = (MyFilterData *)fa->filter_data;
if (mfd->fExpand)
fa->dst.w *= 2;
fa->dst.pitch = (fa->dst.w*4 + 7) & -8;
return FILTERPARAM_SWAP_BUFFERS;
}
Finally, adjust startProc to generate a half lookup table as well, and runProc to allow non-expansion. The decision between halving and thirding color components is folded into the lookup tables, so there's no need to check the flag at runtime.
int tutorialStartProc(FilterActivation *fa, const FilterFunctions *ff) {
MyFilterData *mfd = (MyFilterData *)fa->filter_data;
int i;
if (!(mfd->grn_tab = new Pixel32[256 * 2]))
return 1;
mfd->blu_tab = mfd->grn_tab + 256;
if (mfd->fThird)
for(i=0; i<256; i++) {
mfd->grn_tab[i] = ((0x200 + i) / 3) << 8;
mfd->blu_tab[i] = i / 3;
}
else
for(i=0; i<256; i++) {
mfd->grn_tab[i] = ((0x100 + i) / 2) << 8;
mfd->blu_tab[i] = i / 2;
}
return 0;
}
int tutorialRunProc(const FilterActivation *fa, const FilterFunctions *ff) {
MyFilterData *mfd = (MyFilterData *)fa->filter_data;
PixDim w, h;
Pixel32 *src, *dst;
const Pixel32 *grn_tab = mfd->grn_tab;
const Pixel32 *blu_tab = mfd->blu_tab;
src = (Pixel32 *)fa->src.data;
dst = (Pixel32 *)fa->dst.data;
h = fa->src.h;
do {
w = fa->src.w;
// double the routine for speed; an if would kill us in the
// inner loop, but in the outer loop it's ok
if (mfd->fExpand)
do {
Pixel32 old_pixel, new_pixel;
old_pixel = *src++;
new_pixel = (old_pixel & 0xFF0000)
+ grn_tab[(old_pixel>>8) & 0xff]
+ blu_tab[old_pixel & 0xff];
*dst++ = new_pixel;
*dst++ = new_pixel;
} while(--w);
else
do {
Pixel32 old_pixel, new_pixel;
old_pixel = *src++;
new_pixel = (old_pixel & 0xFF0000)
+ grn_tab[(old_pixel>>8) & 0xff]
+ blu_tab[old_pixel & 0xff];
*dst++ = new_pixel;
} while(--w);
src = (Pixel32 *)((char *)src + fa->src.modulo);
dst = (Pixel32 *)((char *)dst + fa->dst.modulo);
} while(--h);
return 0;
}
Whew! This one involved a bit of coding. Modifying this structure for other filters is easy, though.
back to main page
tutorial[5]: changing the output frame size
tutorial[7]: adding MMX optimizations
VirtualDub external filter SDK 1.05 | ©1999-2001 Avery Lee <phaeron@virtualdub.org> |